# INPUT FORMAT

# mailid
# count for number of name value pairs
# details (2*prev value)
# instruction
# test_start_time
# test_end_time
# count for number of groups, types and questions
# The groups (as much as prev value)
# The qtypes (as much as prev value)
# Questions
# Based on the question type, the format differs
#    CODE
#    question, answer, delimitter
#    MCQRB/MCQCB
#    question, number of options, options
#    FIBQ
#    question
#    PASSAGE
#    question, count
# Question numbers
# total number of questions
# expected answers
# maximum marks
# question indices
# answer indices


# Required
# List of mailids
# List of Questions, expected answers, marks
# Test Data

# Questions
# MCQRB/MCQCB
#    s.no, Question, options, answer, mark, passage_sno
# FIBQ
#    s.no, Question, answer, mark, passage_sno
# PASSAGE
#    s.no, Question
# CODE
#    s,no, Question, input, output, mark

# TEST DATA
# Instructions
# Number of questions per page


# ALGO
# read number of questions per page in numq
# page = 1
# numq_page = 0
# group = []
# type = []
# questions = []
# answers = []
# marks = []
# q_offset = []
# g_offset = []
# g_ctr = 0
# passage_question = False
# for question in questions:
#     type.append (question type)
#     questions.append (question)
#     q_offset.append (len (questions) -1)
#     answer.append (answer)
#     marks.append (marks)
#     numq_page += 1
#     if question type is MCQRB/MCQCB:
#         choices = choices.split (',')
#         questions.extend ([count (choices), choices])
#         g_offset.append (g_ctr)
#         g_ctr += 1
#     if question type is FIBQ:
#         g_offset.append (g_ctr)
#         g_ctr += 1
#     if question type is PASSAGE:
#         passage_question = True
#         passage_count = child_question
#         if numq_page:
#             page += 1
#         questions.append (child_question)
#         g_offset.append (-1)
#     if question type is CODE:
#         g_offset.append (g_ctr)
#         g_ctr += 2
#     group.append (page)
#     if passage_question:
#         passage_count -= 1
#         if not passage_count:
#             page += 1
#             numq_page = 0
#     if numq_page == numq:
#         page += 1
#         numq_page = 0



#INPUT
# USER_FILE = ''
# TEST_DATA_FILE = ''
# QUESTIONS_FILE = ''

import csv
import cgi
import optparse

#Entry point

def main ():

    """
    User Interface is designed here
    """
    
    args_parser = optparse.OptionParser()

    args_parser.add_option ( "-u", "--user-file", action = "store",
                             type = "string", dest = "user_file",
                             help = "File containing user details",
                             default = "")
    args_parser.add_option ( "-t", "--test-file", action = "store",
                             type = "string", dest = "test_data_file",
                             help = "File containing test data",
                             default = "")
    args_parser.add_option ( "-q", "--question-file", action = "store",
                             type = "string", dest = "questions_file",
                             help = "File containing questions",
                             default = "")
    args_parser.add_option ( "-o", "--output-file", action = "store",
                             type = "string", dest = "output_file",
                             help = "Output File",
                             default = "")
    args_parser.add_option ( "--html-sensitive", action = "store_true",
		    	      dest = "is_html_sensitive", default = False,
			      help = "Sensitive to html tags")
    options, args = args_parser.parse_args()

    if not (options.user_file and options.test_data_file and options.questions_file and options.output_file):
        raise Exception, "Please pass user file, test data file, output file and questions file as input"

    generate (options.user_file, options.test_data_file, options.questions_file, options.output_file, options.is_html_sensitive)


def parse_test_data (test_data_file):
    """
        Parses the test data file
        The header of the file is
        Instructions, Num Q. Per Page

        This File should contain just one
        row
    """
    reader = csv.reader (open (test_data_file, 'rb'))
    reader.next () #To read the header
    return reader.next ()

def parse_questions (questions_file, num_q_per_page = 1, is_html_sensitive = False):
    """
        Reads the questions file and constructs the
        questions, options, expected answers and marks
    """
    
    # page number
    # the set of questions with same page number
    # will be displayed in the same page
    page = 1
    # Number of questions in a page
    # This tells as how many questions are so far
    # added to a page
    numq_page = 0
    # The page numbers are passed to the UI in terms
    # called group
    groups = []
    # The types of question
    types = []
    # QUESTIONS
    questions = []
    # expected answers
    expected_answers = []
    # marks
    marks = []
    # Question Number to be displayed in UI
    # passge is not numbered
    q_nos = []
    # Question number incrementer
    q_no = 0
    # the indexes where the questions are actually present
    # in questions list. This is passed to UI for faster
    # retrieval and cohesion
    q_offsets = []
    # these are the indexes where the answer from the candidate
    # would be stored.
    g_offsets = []
    # variable to keep track of given answer offset
    g_ctr = 0
    # Boolean variable to denote if the question that is 
    # currently parsed is a part of passage question or a
    # stand alone question
    passage_question = False
    def parse_and_validate_qtype (q_type):
        q_type = q_type.upper ().strip ()
        if q_type not in ('MCQRB', 'MCQCB', 'FIBQ', 'PASSAGE', 'CODE'):
            raise ValueError, "Invalid Question type %s"%q_type
        return q_type
    reader = csv.DictReader (open (questions_file, 'rb'))

    for question in reader:
        #question type
        q_type = parse_and_validate_qtype (question ['Question Type'])
        types.append (q_type)
        if not is_html_sensitive:
	    questions.append ("<pre>\n" + cgi.escape(question ['Question']) + "\n</pre>")
	else:
	    questions.append (question ['Question'])
        q_offsets.append (str (len (questions) - 1))
        expected_answers.append (question ['Answer'])
        marks.append (question ['Marks'])


        if q_type == 'MCQRB' or q_type == 'MCQCB':
            choices = question ['Choices'].split (',')
            questions.append (str (len (choices)))
            questions.extend (choices)
            g_offsets.append (str (g_ctr))
            g_ctr += 1
            q_no += 1
            q_nos.append (str (q_no))

        elif q_type == 'FIBQ':
            g_offsets.append (str (g_ctr))
            g_ctr += 1
            q_no += 1
            q_nos.append (str (q_no))

        elif q_type == 'PASSAGE':
            passage_question = True
            child_questions = int (question ['Child Questions'])
            if numq_page:
                page += 1
            questions.append (str (child_questions))
            child_questions += 1 # Includeing the passage itself as a question
            g_offsets.append (str (-1)) # No answer for the passage itself
            q_nos.append (str (-1))

        elif q_type == 'CODE':
            questions.append (question ['Choices'])
            if not question ['Delimitter']:
                raise ValueError, 'Empty Delimitter'
            questions.append (question ['Delimitter'])
            expected_answers.append ('') #Source code
            q_no += 1
            q_nos.append (str (q_no))
            g_offsets.append (str (g_ctr))
            g_ctr += 2


        groups.append (str (page))
        numq_page += 1

        if passage_question:
            child_questions -= 1
            if not child_questions:
                page += 1
                numq_page = 0
        elif numq_page == num_q_per_page:
            page += 1
            numq_page = 0

    return groups, types, questions, q_nos, max (q_nos), expected_answers, marks, q_offsets, g_offsets  

def generate (user_file, test_data_file, questions_file, output_file, is_html_sensitive = False):
    instructions, num_q_per_page = parse_test_data (test_data_file)
    groups, types, questions, q_nos, total_qns, expected_answers, marks, q_offsets, g_offsets = parse_questions (questions_file, int (num_q_per_page), is_html_sensitive)
    writer = csv.writer (open (output_file, 'w+'))
    reader = csv.reader (open (user_file, 'rb'))
    header = reader.next ()
    no_of_info = len (header) - 3 #mailid, start_time, end_time
    user = reader.next ()
    while user:
        row = [user [0], 
               no_of_info]
        for index in range (3, len(header)):
            row.append (header [index])
            row.append (user [index])
        row.extend ([instructions,
                     user [1],
                     user [2], 
                     len (groups)] + 
                     groups +  
                     types + 
                     questions +  
                     q_nos +  
                     [total_qns] +  
                     expected_answers +  
                     marks + 
                     q_offsets + 
                     g_offsets)  
        writer.writerow (row)
        writer.writerow (["~!@~!@~!@~!@~!@~!@~!@~!@"])
        try:
            user = reader.next ()
        except StopIteration, e:
            user = None

main ()
